﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using XcoAppSpaces.Core.Ports;
using NUnit.Framework;
using XcoAppSpaces.Core;

namespace test.XcoAppSpaces.Core.Error_Reproductions
{
    [TestFixture]
    public class testSubscriptionWithPredicate
    {
        [Test]
        public void Subscribe_with_predicate_referencing_the_context()
        {
            using (XcoAppSpace server = new XcoAppSpace("tcp.port=12345"))
            using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
            {
				AutoResetEvent signal = new AutoResetEvent(false);
                server.RunWorker<Port<Subscribe<int>>>(new SubscriptionReceiver(signal), "publisher");
                Port<Subscribe<int>> pub = client.ConnectWorker<Port<Subscribe<int>>>("localhost:12345/publisher");

                Port<int> sub = new Port<int>();
                int j = 99;

                Port<Exception> pex = new Port<Exception>();
                pex.RegisterReceiver(ex => Console.WriteLine("*** subscription error: {0}", ex));
                pub.Post(new Subscribe<int>(sub, i => i == j), pex);
                //pub.Post(new Subscribe<int>(sub, i => i == 99), pex);

				Assert.IsTrue(signal.WaitOne(500));
            }
        }

		[Test]
		public void Subscribe_with_predicate_using_instance_field()
		{
			using (XcoAppSpace server = new XcoAppSpace("tcp.port=12345"))
			using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
			{
				AutoResetEvent signal = new AutoResetEvent(false);
				//server.Register<AutoResetEvent>(new AutoResetEvent(false), "signal");
				server.RunWorker<Port<Subscribe<int>>>(new SubscriptionReceiver(signal), "publisher");
				Port<Subscribe<int>> pub = client.ConnectWorker<Port<Subscribe<int>>>("localhost:12345/publisher");

				Port<int> sub = new Port<int>();
				TestValues val = new TestValues();

				Port<Exception> pex = new Port<Exception>();
				pex.RegisterReceiver(ex => Console.WriteLine("*** subscription error: {0}", ex));
				pub.Post(new Subscribe<int>(sub, i => i == val.instanceField), pex);

				Assert.IsTrue(signal.WaitOne(500));
			}
		}

		[Test]
		public void Subscribe_with_predicate_using_static_field()
		{
			using (XcoAppSpace server = new XcoAppSpace("tcp.port=12345"))
			using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
			{
				AutoResetEvent signal = new AutoResetEvent(false);
				//server.Register<AutoResetEvent>(new AutoResetEvent(false), "signal");
				server.RunWorker<Port<Subscribe<int>>>(new SubscriptionReceiver(signal), "publisher");
				Port<Subscribe<int>> pub = client.ConnectWorker<Port<Subscribe<int>>>("localhost:12345/publisher");

				Port<int> sub = new Port<int>();
				
				Port<Exception> pex = new Port<Exception>();
				pex.RegisterReceiver(ex => Console.WriteLine("*** subscription error: {0}", ex));
				pub.Post(new Subscribe<int>(sub, i => i == TestValues.staticField), pex);

				Assert.IsTrue(signal.WaitOne(500));
			}
		}

		[Test]
		public void Subscribe_with_predicate_using_instance_property()
		{
			using (XcoAppSpace server = new XcoAppSpace("tcp.port=12345"))
			using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
			{
				AutoResetEvent signal = new AutoResetEvent(false);
				//server.Register<AutoResetEvent>(new AutoResetEvent(false), "signal");
				server.RunWorker<Port<Subscribe<int>>>(new SubscriptionReceiver(signal), "publisher");
				Port<Subscribe<int>> pub = client.ConnectWorker<Port<Subscribe<int>>>("localhost:12345/publisher");

				Port<int> sub = new Port<int>();
				TestValues val = new TestValues();

				Port<Exception> pex = new Port<Exception>();
				pex.RegisterReceiver(ex => Console.WriteLine("*** subscription error: {0}", ex));
				pub.Post(new Subscribe<int>(sub, i => i == val.InstanceProp), pex);

				Assert.IsTrue(signal.WaitOne(500));
			}
		}

		[Test]
		public void Subscribe_with_predicate_using_static_property()
		{
			using (XcoAppSpace server = new XcoAppSpace("tcp.port=12345"))
			using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
			{
				AutoResetEvent signal = new AutoResetEvent(false);
				//server.Register<AutoResetEvent>(new AutoResetEvent(false), "signal");
				server.RunWorker<Port<Subscribe<int>>>(new SubscriptionReceiver(signal), "publisher");
				Port<Subscribe<int>> pub = client.ConnectWorker<Port<Subscribe<int>>>("localhost:12345/publisher");

				Port<int> sub = new Port<int>();
				
				Port<Exception> pex = new Port<Exception>();
				pex.RegisterReceiver(ex => Console.WriteLine("*** subscription error: {0}", ex));
				pub.Post(new Subscribe<int>(sub, i => i == TestValues.StaticProp), pex);

				Assert.IsTrue(signal.WaitOne(500));
			}
		}

		/*[Test]
		public void Subscribe_with_predicate_using_instance_method()
		{
			using (XcoAppSpace server = new XcoAppSpace("tcp.port=12345"))
			using (XcoAppSpace client = new XcoAppSpace("tcp.port=0"))
			{
				server.Register<AutoResetEvent>(new AutoResetEvent(false), "signal");
				server.RunWorker<Port<Subscribe<int>>, SubscriptionReceiver>("publisher");
				Port<Subscribe<int>> pub = client.ConnectWorker<Port<Subscribe<int>>>("localhost:12345/publisher");

				Port<int> sub = new Port<int>();
				TestValues val = new TestValues();

				Port<Exception> pex = new Port<Exception>();
				client.Receive(pex, ex => Console.WriteLine("*** subscription error: {0}", ex));
				pub.Post(new Subscribe<int>(sub, i => i == val.InstanceMethod()), pex); // this does not work

				Assert.IsTrue(server.Resolve<AutoResetEvent>("signal").WaitOne(500));
			}
		}*/
    
        internal class SubscriptionReceiver : Port<Subscribe<int>>
        {
        	private AutoResetEvent signal;

			public SubscriptionReceiver(AutoResetEvent signal)
			{
				this.signal = signal;
			}


            [XcoConcurrent]
            void Process(Subscribe<int> subscription)
            {
                Console.WriteLine("received sub");
				if (subscription.MatchesFilter(99))
				{
					Console.WriteLine("predicate working correctly");
					this.signal.Set();
				}
				else
					Console.WriteLine("Error: received predicate not working!!");
            }
        }

		internal class TestValues
		{
			public int instanceField = 99;

			public int InstanceProp
			{
				get { return instanceField; }
			}

			public int InstanceMethod()
			{
				return instanceField;
			}

			public static int staticField = 99;

			public static int StaticProp
			{
				get { return staticField; }
			}

			public static int StaticMethod()
			{
				return staticField;
			}

		}
    }
}
